/*
 * 版权所有：义乌市路比信息科技有限公司
 * 作者：kinwyb<http://www.lcfgly.com>
 * 日期：2015-11-23
 */
package lcfgly.mybatis.auto;

import java.util.List;
import java.util.Properties;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import static org.apache.ibatis.reflection.SystemMetaObject.DEFAULT_OBJECT_FACTORY;
import static org.apache.ibatis.reflection.SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY;
import org.apache.logging.log4j.LogManager;

/**
 *
 * @author kinwyb<http://www.lcfgly.com>
 * @datetime 2015-11-23 13:50:08
 * @version 1.0
 */
public abstract class StatementHandlerInterceptor implements Interceptor {
    
    org.apache.logging.log4j.Logger logger=LogManager.getLogger(this.getClass());
    
    /**
     * 分离出实际的代理对象MetaObject
     *
     * @param statementHandler
     * @return
     */
    protected MetaObject getMetaObject(StatementHandler statementHandler) {
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        // 分离代理对象链  
        while (metaStatementHandler.hasGetter("h")) {
            Object object = metaStatementHandler.getValue("h");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, metaStatementHandler.getReflectorFactory());
        }
        // 分离最后一个代理对象的目标类  
        while (metaStatementHandler.hasGetter("target")) {
            Object object = metaStatementHandler.getValue("target");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, metaStatementHandler.getReflectorFactory());
        }
        return metaStatementHandler;
    }

    /**
     * 获取MappedStatement
     * @param metaStatementHandler
     * @return 
     */
    protected MappedStatement getMappedStatement(MetaObject metaStatementHandler){
       return (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
    }
    
    /**
     * 获取BoundSQL
     * @param metaStatementHandler
     * @return 
     */
    protected BoundSql getBoundSQL(MetaObject metaStatementHandler){
        return (BoundSql)metaStatementHandler.getValue("delegate.boundSql");
    }
    
    /**
     * 获取Mappper中方法名称
     * @param metaStatementHandler
     * @return 
     */
    protected String getMethodName(MetaObject metaStatementHandler){
        MappedStatement m=getMappedStatement(metaStatementHandler);
        return m.getId().substring(m.getId().lastIndexOf(".") + 1);
    }
    
    /**
     * 更新sql
     * @param metaStatementHandler 
     * @param sql 新sql语句
     * @param parameterObject 参数mybatis会根据类型决定使用哪个TypeHandler。比如：参数类型是String的话就用StringTypeHandler
     */
    protected void updateSql(MetaObject metaStatementHandler, String sql, Object parameterObject) {
        SqlSourceBuilder builder = new SqlSourceBuilder(getMappedStatement(metaStatementHandler).getConfiguration());  
        SqlSource sqlSource = builder.parse(sql, parameterObject.getClass(), null);
        List<ParameterMapping> parameterMappings = sqlSource.getBoundSql(parameterObject).getParameterMappings();
        metaStatementHandler.setValue("delegate.boundSql.sql", sqlSource.getBoundSql(parameterObject).getSql());
        metaStatementHandler.setValue("delegate.boundSql.parameterMappings", parameterMappings);
    }
    
    /**
     * 获取参数Map
     * @param metaStatementHandler
     * @return 
     */
    protected ParamMap getParameterMap(MetaObject metaStatementHandler){
        Object param=getBoundSQL(metaStatementHandler).getParameterObject();
        MapperMethod.ParamMap params;
        if(param instanceof MapperMethod.ParamMap){
            params=(MapperMethod.ParamMap)param;
        }else{
            params=new MapperMethod.ParamMap();
            params.put("0", param);
        }
        return params;
    }
    
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {}

}
